package
{
	import flash.display.NativeWindow;
	import flash.display.NativeWindowResize;
	import flash.display.StageDisplayState;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.net.SharedObject;
	import flash.net.SharedObjectFlushStatus;
	import flash.system.Capabilities;
	import flash.ui.Mouse;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	
	import mx.core.FlexGlobals;

	public class CommanderWindowStateMgr
	{
		//
		// The CommanderWindowStateMgr is a singleton manager class that controls the native application window state.
		//
		
		public function CommanderWindowStateMgr(enforcer:SingletonEnforcer) { ; }
		private static var m_mgr:CommanderWindowStateMgr;
		
		public static function getInstance():CommanderWindowStateMgr
		{
			if( m_mgr == null) {
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_LOW, "Creating new CommanderWindowStateMgr instance." );
				m_mgr = new CommanderWindowStateMgr(new SingletonEnforcer());
			}
			return m_mgr;
		}

		//
		// Screen state is persisted in a local shared object.
		//
		
		private var m_sharedObject:SharedObject;
		private var m_idleWatchTimer:Timer;
		private var m_lastEvent:int;
		
		public function initialize():void
		{
			TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "CommanderWindowStateMgr::initialize" );

			// Since the app is entirely chromeless and we want an autoshow controls layer, we have to manage moves and resizes
			// and track whether or not we need to show the control layer or manage the mouse cursor.
			var cMgr:LRCommanderMgr = LRCommanderMgr.getInstance();
			cMgr.animationSurface.addEventListener(MouseEvent.MOUSE_DOWN, startMoveOrResize);
			cMgr.animationSurface.addEventListener(MouseEvent.MOUSE_UP, endMoveOrResize);
			cMgr.animationSurface.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveOutOver);
			cMgr.animationSurface.addEventListener(MouseEvent.ROLL_OVER, mouseMoveOutOver);
			cMgr.animationSurface.addEventListener(MouseEvent.ROLL_OUT, mouseMoveOutOver);


			// Start the idle watchdog timer.  This controls whether the overlay layer is shown or hidden.
			m_idleWatchTimer = new Timer( 1000 );
			m_idleWatchTimer.addEventListener(TimerEvent.TIMER, idleWatcher );
			m_idleWatchTimer.start();

			try
			{
				var m_sharedObjectName:String = "LRCommander-ScreenState";
				m_sharedObject = SharedObject.getLocal(m_sharedObjectName);
			}
			catch(e:Error)
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_HIGH, "Couldn't load the screen state shared object: ", e);
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_HIGH, "Not persisting screen state for this session.");
			}
			
			resetWindowPosition(false);
			loadPersistedScreenState();
			positionWindow();
		}

		private var window:NativeWindow = FlexGlobals.topLevelApplication.stage.nativeWindow;

		private var m_isFullScreen:Boolean;
		private var m_lastWidth:int;
		private var m_lastHeight:int;
		private var m_lastX:int;
		private var m_lastY:int;
		
		private function loadPersistedScreenState():void
		{
			if( m_sharedObject != null )
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "Loading persisted screen state" );

				if( m_sharedObject.data.IsFullScreen != null )
					m_isFullScreen = m_sharedObject.data.IsFullScreen;
				if( m_sharedObject.data.LastWidth != null )
					m_lastWidth = m_sharedObject.data.LastWidth
				if( m_sharedObject.data.LastHeight != null )
					m_lastHeight = m_sharedObject.data.LastHeight;
				if( m_sharedObject.data.LastXPos != null )
					m_lastX = m_sharedObject.data.LastXPos;
				if( m_sharedObject.data.LastYPos != null )
					m_lastY = m_sharedObject.data.LastYPos;
			}
		}

		private function savePersistedScreenState():void
		{
			if( m_sharedObject != null )
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "Saving persisted screen state" );

				m_sharedObject.setProperty("IsFullScreen", m_isFullScreen);
				m_sharedObject.setProperty("LastWidth", m_lastWidth);
				m_sharedObject.setProperty("LastHeight", m_lastHeight );
				m_sharedObject.setProperty("LastXPos", m_lastX );
				m_sharedObject.setProperty("LastYPos", m_lastY );
				m_sharedObject.flush();
			}
		}
		
		private function positionWindow():void
		{
			if( m_isFullScreen )
			{		
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "positionWindow: fullscreen " );

				window.x = 0;
				window.y = 0;
				FlexGlobals.topLevelApplication.width = Capabilities.screenResolutionX;
				FlexGlobals.topLevelApplication.height = Capabilities.screenResolutionY;
				FlexGlobals.topLevelApplication.stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
				m_manageCursor = true;
				
				// BUGBUG: no matter what I try, this never causes the mouse cursor to hide in the case where we're restoring
				// full screen mode automatically at application launch.  I've tried a direct call to Mouse.hide *and* a timer 
				// driven delayed call.  Neither work in this case.
				//
				// Everything works as expected after any mouse events have fired in the application, so my guess is that it's
				// probably an initialization state bug in AIR.
				
				Mouse.hide();
			}
			else
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM,
					"positionWindow:","x=",m_lastX,"y=",m_lastY,"w=",m_lastWidth,"h=",m_lastHeight );

				window.x = m_lastX;
				window.y = m_lastY;
				FlexGlobals.topLevelApplication.width = m_lastWidth;
				FlexGlobals.topLevelApplication.height = m_lastHeight;
				FlexGlobals.topLevelApplication.stage.displayState = StageDisplayState.NORMAL;
				Mouse.show();
			}
		}
		
		public function toggleFullScreen(event:MouseEvent):void
		{	
			TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "CommanderWindowStateMgr::toggleFullScreen" );
			
			if( false == m_isFullScreen )
			{
				m_lastX = window.x;
				m_lastY = window.y;
				m_lastWidth = window.width;
				m_lastHeight = window.height;
				m_isFullScreen = true;
			}
			else
			{
				m_isFullScreen = false;
			}
			
			savePersistedScreenState();
			positionWindow();
		}

		public function resetWindowPosition(reposition:Boolean=true):void
		{
			m_isFullScreen = false;
			m_lastWidth = 640;
			m_lastHeight = 480;
			m_lastX = 50;
			m_lastY = 50;
			
			if( reposition )
				positionWindow();		
		}

		private function startMoveOrResize(e:MouseEvent):void
		{
			if( false == m_isFullScreen )
			{
				var edgeThreshold:uint = 25;
				var resizeFrom:String;
				
				if( FlexGlobals.topLevelApplication.mouseY <= edgeThreshold )
					resizeFrom = NativeWindowResize.TOP;
				else if ( ( window.height - FlexGlobals.topLevelApplication.mouseY ) <= edgeThreshold )
					resizeFrom = NativeWindowResize.BOTTOM;
				else
					resizeFrom = "";
							
				if( FlexGlobals.topLevelApplication.mouseX <= edgeThreshold )
					resizeFrom += NativeWindowResize.LEFT;
				else if ( ( window.width - FlexGlobals.topLevelApplication.mouseX ) <= edgeThreshold )
					resizeFrom += NativeWindowResize.RIGHT;
	
				// If the mouse isn't within the threshold of any edge, this is a move... not a resize.
				if( resizeFrom == "" )
				{
					TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "startMoveOrResize: MOVE" );
					FlexGlobals.topLevelApplication.stage.nativeWindow.startMove();
				}
				else
				{
					TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "startMoveOrResize: Resize, mode=", resizeFrom );
					FlexGlobals.topLevelApplication.stage.nativeWindow.startResize(resizeFrom);
				}
			}
		}
		
		private function endMoveOrResize(e:MouseEvent):void
		{
			// BUGBUG: Maybe better to use a Resized event?
			if( false == m_isFullScreen )
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM,
					"endMoveOrResize:","x=",window.x,"y=",window.y,"w=",window.width,"h=",window.height );

				m_lastX = window.x;
				m_lastY = window.y;
				m_lastWidth = window.width;
				m_lastHeight = window.height;
			
				savePersistedScreenState();
			}
		}

		private var m_manageCursor:Boolean = false;
		
		// This is the duration of the controls layer fade effect.
		private var m_controlsFadeDuration:uint = 500;
		private var m_mouseDelayedHideDuration:uint = 300;
		public function get controlsFadeDuration():uint { return m_controlsFadeDuration; }
		
		private function idleWatcher(e:TimerEvent):void
		{
			var currentTime:int = getTimer();
			if( ( currentTime - m_lastEvent ) > 2000 )
			{
				if( FlexGlobals.topLevelApplication.currentState == "overlay" )
				{
					TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "No mouse events - returning application to normal state" );
					FlexGlobals.topLevelApplication.currentState = "normal";
					
					// If we're managing the cursor, schedule a timer to hide the cursor just as the fade out transition completes.
					if( m_manageCursor )
					{
						var hideCursorTimer:Timer = new Timer( m_mouseDelayedHideDuration );
						hideCursorTimer.repeatCount = 1;
						hideCursorTimer.addEventListener(TimerEvent.TIMER, doDelayedHide);
						hideCursorTimer.start();						
					}
				}
			}
		}

		private function doDelayedHide(e:TimerEvent):void
		{
			TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "doDelayedHide - hiding the mouse cursor" );
			// If we're still managing the cursor and in the normal state, hide the cursor.
			if( m_manageCursor &&
				(FlexGlobals.topLevelApplication.currentState == "normal") )
			{
				Mouse.hide();
			}
			else
			{
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_HIGH, 
					"doDelayedHide: we got called, but we're either not managing the cursor anymore, or the app is not in the mouseless state anymore?" );
			}
		}
		
		private function mouseMoveOutOver(e:MouseEvent):void
		{
			// Record the time of the last mouse event so we can figure out when we hit the idle threshold.
			m_lastEvent = getTimer();
			
			if( e.type == MouseEvent.MOUSE_MOVE )
			{
				
				if( FlexGlobals.topLevelApplication.currentState == "normal" )
				{
					TraceManager.trace_event(TraceManager.WINDOW_STATE_MGR,TraceManager.TL_MEDIUM, e);
					TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "mouseMoveOutOver: MOUSE_MOVE - showing overlay" );

					FlexGlobals.topLevelApplication.currentState = "overlay";

					// Show the cursor if we're managing the cursor.
					if( m_manageCursor )
						Mouse.show();
				}

			}
			else if( ( e.type == MouseEvent.ROLL_OUT ) && ( e.target == LRCommanderMgr.getInstance().animationSurface ) )
			{
				// If at any point the cursor leaves the window, then make sure we're showing the cursor and stop managing it.
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "mouseMoveOutOver: ROLL_OUT - stop managing the cursor" );
				m_manageCursor = false;
				Mouse.show();
			}
			else if( e.type == MouseEvent.ROLL_OVER )
			{
				// If at any point the mouse returns to the window, resume managing the cursor and if we're in the normal state, hide it.
				TraceManager.trace_msg(TraceManager.WINDOW_STATE_MGR, TraceManager.TL_MEDIUM, "mouseMoveOutOver: ROLL_OVER - resume managing the cursor" );
				m_manageCursor = true;
				if( FlexGlobals.topLevelApplication.currentState == "normal")
					Mouse.hide();
			}
		}		

	}
}

class SingletonEnforcer { }